home *** CD-ROM | disk | FTP | other *** search
Text File | 2000-10-06 | 15.1 KB | 491 lines | [TEXT/CWIE] |
- /*
- File: PPCToolboxKeychain.c
-
- Contains: 'osax' for creating PPC Toolbox keys in the keychain.
-
- Written by: Quinn "The Eskimo!"
-
- Copyright: Copyright © 2000 by Apple Computer, Inc., All Rights Reserved.
-
- Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
- ("Apple") in consideration of your agreement to the following terms, and your
- use, installation, modification or redistribution of this Apple software
- constitutes acceptance of these terms. If you do not agree with these terms,
- please do not use, install, modify or redistribute this Apple software.
-
- In consideration of your agreement to abide by the following terms, and subject
- to these terms, Apple grants you a personal, non-exclusive license, under Apple’s
- copyrights in this original Apple software (the "Apple Software"), to use,
- reproduce, modify and redistribute the Apple Software, with or without
- modifications, in source and/or binary forms; provided that if you redistribute
- the Apple Software in its entirety and without modifications, you must retain
- this notice and the following text and disclaimers in all such redistributions of
- the Apple Software. Neither the name, trademarks, service marks or logos of
- Apple Computer, Inc. may be used to endorse or promote products derived from the
- Apple Software without specific prior written permission from Apple. Except as
- expressly stated in this notice, no other rights or licenses, express or implied,
- are granted by Apple herein, including but not limited to any patent rights that
- may be infringed by your derivative works or by other works in which the Apple
- Software may be incorporated.
-
- The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
- WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
- WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
- COMBINATION WITH YOUR PRODUCTS.
-
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
- OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
- (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- Change History (most recent first):
-
- */
-
- /////////////////////////////////////////////////////////////////
-
- // MIB Setup
-
- #include "MoreSetup.h"
-
- // Mac OS Interfaces
-
- #include <Keychain.h>
- #include <CodeFragments.h>
- #include <StringCompare.h>
- #include <OpenTransportProviders.h>
-
- // Standard C interfaces
-
- #include <ctype.h>
- #include <stdlib.h>
-
- // MIB interfaces
-
- #include "MoreAppleEvents.h"
-
- // Apple Event Constants
-
- #include "PPCToolboxKeychain.h"
-
- /////////////////////////////////////////////////////////////////
- #pragma mark ----- Global Data -----
-
- static FSSpec gFragmentFSSpec; // location of osax file
-
- static AEEventHandlerUPP gBlessKeyHandlerUPP = nil; // -> BlessKeyHandlerProc
-
- static Boolean gHaveInstalled = false; // have we installed our event handler
-
- __declspec(export) UInt32 gAdditionReferenceCount = 0; // required by AppleScript to prevent unload while busy
-
- /////////////////////////////////////////////////////////////////
- #pragma mark ----- Event Handlers -----
-
- static Boolean CheckForChars(const char **cursor, const char *templ)
- // Assuming the *cursor is a pointer to an input string and
- // templ is a pointer to a constant template string,
- // check that *cursor starts with the characters in the template,
- // return true if the template is found or false otherwise.
- // Also updates *cursor as it ‘consumes’ characters.
- //
- // For example, if *cursor points to "eppc://foo" and
- // templ points to "eppc://", this will return true
- // and update *cursor to point to the the "f" in "foo".
- {
- MoreAssertQ(cursor != nil);
- MoreAssertQ(*cursor != nil);
- MoreAssertQ(templ != nil);
-
- while (**cursor == *templ && *templ != 0) {
- *cursor += 1;
- templ += 1;
- }
- return *templ == 0;
- }
-
- static Boolean CollectChars(const char **cursor, char terminator, UInt8 *buffer, ByteCount bufferSize, ByteCount *actualSize)
- // Copies characters from an input string (pointed to be *cursor ) to an output buffer
- // (described by buffer and bufferSize), terminating when either a) we hit a null terminator
- // in *cursor, or when we hit the terminator character in the input string. Updates
- // *cursor to point to the terminating character. Also returns, in *actualSize, the number
- // of bytes that were copied to the buffer. Converts escape sequences (%xx) into the
- // appropriate character in the output buffer.
- //
- // Returns true if the copy was successful, or false if it failed. Reasons for failure
- // include a) running out of space in the output buffer, b) a bogus escape sequence, or
- // c) not copying any characters to the output buffer (null output is not allowed).
- {
- Boolean result;
-
- MoreAssertQ(cursor != nil);
- MoreAssertQ(*cursor != nil);
- MoreAssertQ(buffer != nil);
- MoreAssertQ(actualSize != nil);
-
- *actualSize = 0;
-
- result = true;
- do {
-
- // If we’re run out of input characters, leave with result true.
-
- if ( **cursor == 0 || **cursor == terminator ) {
- break;
- }
-
- // We know we’re going for another output character. If we don’t have enough
- // space, fail with an error.
-
- if ( *actualSize == bufferSize ) {
- result = false;
- break;
- }
-
- // Now look for a '%'; if we find one, interpret the digits that follow as hex.
- // Otherwise just copy the character across.
-
- if (**cursor == '%') {
- char tmpStr[3];
-
- *cursor += 1; // skip '%'
-
- tmpStr[0] = (*cursor)[0];
- tmpStr[1] = (*cursor)[1];
- tmpStr[2] = 0;
-
- if ( ! isxdigit(tmpStr[0]) || ! isxdigit(tmpStr[1]) ) {
- result = false;
- break;
- }
- *cursor += 2; // skip hex digits
-
- buffer[*actualSize] = (UInt8) strtoul(tmpStr, nil, 16);;
- } else {
- buffer[*actualSize] = (UInt8) **cursor;
- *cursor += 1;
- }
- *actualSize += 1;
-
- } while (true);
-
- // If we didn’t copy across /any/ characters, that’s an error.
-
- if (result && *actualSize == 0) {
- result = false;
- }
-
- return result;
- }
-
- static OSStatus ParseURLIntoGenericData(const char *url, UInt8 *genaBuffer, ByteCount *genaBufferSize)
- // Parses strings of the form eppc://dns.name or eppc:/at/machine:zone
- // into the appropriate 'gena' attribute format (either a packed NBPEntity
- // or a LocationNameRec with locationKindSelector of ppcXTIAddrLocation).
- {
- OSStatus err;
- const char *cursor;
- LocationNamePtr loc;
- ByteCount actualSize;
- UInt8 *outputCursor;
-
- MoreAssertQ(url != nil);
- MoreAssertQ(genaBuffer != nil);
- MoreAssertQ(genaBufferSize != nil);
-
- err = -1;
-
- cursor = url;
-
- if ( ! CheckForChars(&cursor, "eppc:/") ) goto parseError;
-
- switch (*cursor) {
- case '/':
- // eppc://dns.name
-
- // For TCP/IP, the 'gena' attribute for a PPC Toolbox key is in the
- // same LocationNameRec format used when sending Apple events over TCP/IP.
- // See DTS Technote 1176 for more details.
- //
- // <http://developer.apple.com/technotes/tn/tn1176.html#ppctoolbox>
-
- cursor += 1;
-
- loc = (LocationNamePtr) genaBuffer;
- loc->locationKindSelector = ppcXTIAddrLocation;
- loc->u.xtiType.Reserved[0] = 0;
- loc->u.xtiType.Reserved[1] = 0;
- loc->u.xtiType.Reserved[2] = 0;
- loc->u.xtiType.xtiAddr.fAddressType = AF_DNS;
-
- if ( ! CollectChars(&cursor, 0, loc->u.xtiType.xtiAddr.fAddress, sizeof(loc->u.xtiType.xtiAddr.fAddress), &actualSize) ) goto parseError;
- if ( *cursor != 0) goto parseError;
-
- loc->u.xtiType.xtiAddrLen = sizeof(PPCXTIAddressType) + actualSize;
-
- *genaBufferSize = loc->u.xtiType.xtiAddrLen + sizeof(loc->locationKindSelector)
- + sizeof(loc->u.xtiType.Reserved)
- + sizeof(loc->u.xtiType.xtiAddrLen);
- break;
- case 'a':
- // eppc:/at/machine:zone
-
- // For AppleTalk, the 'gena' attribute for a PPC Toolbox key is in the
- // standard NBPEntity format (ie three packed Pascal strings, each of
- // 32 characters or less).
-
- if ( ! CheckForChars(&cursor, "at/") ) goto parseError;
-
- outputCursor = genaBuffer;
-
- if ( ! CollectChars(&cursor, ':', outputCursor + 1, 32, &actualSize) ) goto parseError;
- if ( *cursor != ':') goto parseError;
- cursor += 1;
-
- *outputCursor = actualSize;
- outputCursor += *outputCursor + 1;
-
- BlockMove("\pPPCToolBox", outputCursor, 11);
- outputCursor += 11;
-
- if ( ! CollectChars(&cursor, '0', outputCursor + 1, 32, &actualSize) ) goto parseError;
- if ( *cursor != 0) goto parseError;
-
- *outputCursor = actualSize;
- outputCursor += *outputCursor + 1;
-
- *genaBufferSize = outputCursor - genaBuffer;
- break;
- default:
- goto parseError;
- }
-
- err = noErr;
-
- parseError:
-
- return err;
- }
-
- static pascal OSErr BlessKeyHandlerProc(const AppleEvent *theAppleEvent, AppleEvent *reply, UInt32 handlerRefcon)
- // The Apple event handler installed by our osax's fragment initialisation routine.
- // This handler extracts the event parameters ("name" and "addr"), looks up the
- // generic key by name in the default keychain, then parses the "addr" URL into
- // a 'gena' key attribute and stores that attribute in the key.
- {
- OSStatus err;
- OSStatus junk;
- AEDesc nameDesc;
- AEDesc addrDesc;
- Str255 name;
- char addr[256];
- KCItemRef foundItem;
- UInt8 genaBuffer[256];
- ByteCount genaBufferSize;
-
- MoreAssertQ(theAppleEvent != nil);
- MoreAssertQ(reply != nil);
- #if MORE_DEBUG
- MoreAssertQ(handlerRefcon == 0);
- #else
- #pragma unused(handlerRefcon)
- #endif
-
- gAdditionReferenceCount += 1; // tell AppleScript we’re busy
-
- MoreAENullDesc(&nameDesc);
- MoreAENullDesc(&addrDesc);
- foundItem = nil;
-
- err = noErr;
- if ( !(KeychainManagerAvailable()) ) {
- err = unimpErr;
- }
- err = AEGetParamDesc(theAppleEvent, keyAEName, typeText, &nameDesc);
- if (err == noErr) {
- err = MoreAEGetPStringFromDescriptor(&nameDesc, name);
- }
- if (err == noErr) {
- err = AEGetParamDesc(theAppleEvent, kAEISClientAddress, typeText, &addrDesc);
- }
- if (err == noErr) {
- err = MoreAEGetCStringFromDescriptor(&addrDesc, addr);
- }
- if (err == noErr) {
- err = MoreAEGotRequiredParams(theAppleEvent);
- }
- if (err == noErr) {
- err = KCFindGenericPassword(name, nil, 0, nil, 0, &foundItem);
- }
- if (err == noErr) {
- genaBufferSize = sizeof(genaBuffer);
- err = ParseURLIntoGenericData(addr, genaBuffer, &genaBufferSize);
- }
- if (err == noErr) {
- KCAttribute attr;
-
- attr.tag = kGenericKCItemAttr;
- attr.length = genaBufferSize;
- attr.data = genaBuffer;
- err = KCSetAttribute(foundItem, &attr);
- }
- if (err == noErr) {
- err = KCUpdateItem(foundItem);
- }
-
- if (reply->dataHandle != nil) {
- // Need to put the an error string into the keyErrorString parameter of
- // reply here. Only necessary for errors that AppleScript doesn’t already
- // know about, such as the unimpErr we generate if Keychain isn't available.
- // This code is left as an exercise for the reader.
- }
-
- if (foundItem != nil) {
- junk = KCReleaseItem(&foundItem);
- MoreAssertQ(junk == noErr);
- }
- MoreAEDisposeDesc(&nameDesc);
- MoreAEDisposeDesc(&addrDesc);
-
- gAdditionReferenceCount -= 1; // tell AppleScript we’re no longer busy
-
- return err;
- }
-
- /////////////////////////////////////////////////////////////////
- #pragma mark ----- Init/Term -----
-
- extern OSErr FragmentInit(const CFragInitBlock *initBlock);
- extern void FragmentTerm(void);
-
- extern OSErr FragmentInit(const CFragInitBlock *initBlock)
- // This routine is the native osax's fragment initialization
- // routine. It’s responsible for installing our event handler.
- {
- OSStatus err;
-
- #if MORE_DEBUG
- DebugStr("\pFragmentInit");
- #endif
-
- MoreAssertQ(gAdditionReferenceCount == 0); // Either the data initialization failed, or something else bad has happened.
-
- // We latch the FSSpec of the 'osax' file in case we need to access
- // our resource fork. We currently don’t use this info, but a future
- // extensions might use it (to get better error numbers, for example).
-
- MoreAssertQ(initBlock->fragLocator.where == kDataForkCFragLocator);
- gFragmentFSSpec = *(initBlock->fragLocator.u.onDisk.fileSpec);
-
- // Install the handler.
-
- err = noErr;
- gBlessKeyHandlerUPP = NewAEEventHandlerUPP(BlessKeyHandlerProc);
- if (gBlessKeyHandlerUPP == nil) {
- err = memFullErr;
- }
- if (err == noErr) {
- err = AEInstallEventHandler(kBlessKeyClassID, kBlessKeyEventID, gBlessKeyHandlerUPP, 0, true);
- gHaveInstalled = (err == noErr);
- }
-
- // Clean up.
-
- if (err != noErr) {
- FragmentTerm();
- }
-
- return err;
- }
-
- extern void FragmentTerm(void)
- // This routine is the native osax's fragment termination
- // routine. It’s responsible for tearing down any initialization
- // done by FragmentInit.
- {
- OSStatus junk;
-
- #if MORE_DEBUG
- DebugStr("\pFragmentTerm");
- #endif
-
- MoreAssertQ(gAdditionReferenceCount == 0); // Why are we being unloaded if we’re busy?
-
- if (gHaveInstalled) {
- MoreAssertQ(gBlessKeyHandlerUPP != nil);
- junk = AERemoveEventHandler(kBlessKeyClassID, kBlessKeyEventID, gBlessKeyHandlerUPP, true);
- MoreAssertQ(junk == noErr);
- gHaveInstalled = false;
- }
- if (gBlessKeyHandlerUPP != nil) {
- DisposeAEEventHandlerUPP(gBlessKeyHandlerUPP);
- gBlessKeyHandlerUPP = nil;
- }
- }
-
- #ifdef PPCTOOLBOX_KEYCHAIN_TESTBED
-
- // The code inside this conditional is only used by the Testbed
- // target to create a testbed application that let’s us debug
- // the core of the 'osax' without having to mess around debugging
- // code resources.
-
- #include <stdio.h>
-
- static void TestParseURL(const char *url)
- {
- OSStatus err;
- UInt8 genaBuf[256];
- ByteCount genaBufSize;
- ByteCount thisByteIndex;
-
- genaBufSize = sizeof(genaBuf);
- err = ParseURLIntoGenericData(url, genaBuf, &genaBufSize);
- if (err == noErr) {
- printf("ParseURLIntoGenericData(%s) = \n", url);
- for (thisByteIndex = 0; thisByteIndex < genaBufSize; thisByteIndex++) {
- printf("%02x ", genaBuf[thisByteIndex]);
- }
- printf("\n");
- } else {
- printf("••• ParseURLIntoGenericData(%s), err = %ld\n", url, err);
- }
- }
-
- extern void main(void)
- {
- printf("PPCToolboxKeychain Testbed!\n");
-
- // √ means test should succeed
- // x means test should fail
-
- TestParseURL("eppc://guy-smiley.apple.com"); // √
- TestParseURL("eppc:/at/Guy Smiley:*"); // √
- TestParseURL("foo"); // x
- TestParseURL("eppc:"); // x
- TestParseURL("eppc:/"); // x
- TestParseURL("eppc://"); // x
- TestParseURL("eppc:/bob/"); // x
- TestParseURL("eppc:/ab/"); // x
- TestParseURL("eppc:/at/"); // x
- TestParseURL("eppc:/at/machine"); // x
- TestParseURL("eppc:/at/machine:"); // x
- TestParseURL("eppc:/at/:"); // x
- TestParseURL("eppc:/at/:zone"); // x
- TestParseURL("eppc:/at/machine:zone"); // √
- TestParseURL("eppc:/at/machine%3Awith%3Acolons:zone"); // √
- TestParseURL("eppc:/at/01234567890123456789012345678901:zone"); // √
- TestParseURL("eppc:/at/012345678901234567890123456789012:zone"); // x
- TestParseURL("eppc://%"); // x
- TestParseURL("eppc://%x"); // x
- TestParseURL("eppc://%xy"); // x
-
- printf("Done. Press command-Q to Quit.\n");
- }
-
- #endif
-